/** @file
  The implementation of dump policy entry function in IpSecConfig application.

  Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "IpSecConfig.h"
#include "Dump.h"
#include "ForEach.h"
#include "Helper.h"

/**
  Private function called to get the version infomation from an EFI_IP_ADDRESS_INFO structure.

  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.

  @return the value of version.
**/
UINTN
GetVerFromAddrInfo (
  IN EFI_IP_ADDRESS_INFO    *AddressInfo
)
{
  if((AddressInfo->PrefixLength <= 32) && (AddressInfo->Address.Addr[1] == 0) &&
     (AddressInfo->Address.Addr[2] == 0) && (AddressInfo->Address.Addr[3] == 0)) {
    return IP_VERSION_4;
  } else {
    return IP_VERSION_6;
  }
}

/**
  Private function called to get the version information from a EFI_IP_ADDRESS structure.

  @param[in] Address    The pointer to the EFI_IP_ADDRESS structure.

  @return The value of the version.
**/
UINTN
GetVerFromIpAddr (
  IN EFI_IP_ADDRESS    *Address
)
{
  if ((Address->Addr[1] == 0) && (Address->Addr[2] == 0) && (Address->Addr[3] == 0)) {
    return IP_VERSION_4;
  } else {
    return IP_VERSION_6;
  }
}

/**
  Private function called to print an ASCII string in unicode char format.

  @param[in] Str       The pointer to the ASCII string.
  @param[in] Length    The value of the ASCII string length.
**/
VOID
DumpAsciiString (
  IN CHAR8    *Str,
  IN UINTN    Length
  )
{
  UINTN    Index;
  Print (L"\"");
  for (Index = 0; Index < Length; Index++) {
    Print (L"%c", (CHAR16) Str[Index]);
  }
  Print (L"\"");
}

/**
  Private function called to print a buffer in Hex format.

  @param[in] Data      The pointer to the buffer.
  @param[in] Length    The size of the buffer.

**/
VOID
DumpBuf (
  IN UINT8    *Data,
  IN UINTN    Length
  )
{
  UINTN    Index;
  for (Index = 0; Index < Length; Index++) {
    Print (L"%02x ", Data[Index]);
  }
}

/**
  Private function called to print EFI_IP_ADDRESS_INFO content.

  @param[in] AddressInfo    The pointer to the EFI_IP_ADDRESS_INFO structure.
**/
VOID
DumpAddressInfo (
  IN EFI_IP_ADDRESS_INFO    *AddressInfo
  )
{
  if (IP_VERSION_4 == GetVerFromAddrInfo (AddressInfo)) {
    Print (
      L"%d.%d.%d.%d",
      (UINTN) AddressInfo->Address.v4.Addr[0],
      (UINTN) AddressInfo->Address.v4.Addr[1],
      (UINTN) AddressInfo->Address.v4.Addr[2],
      (UINTN) AddressInfo->Address.v4.Addr[3]
      );
    if (AddressInfo->PrefixLength != 32) {
      Print (L"/%d", (UINTN) AddressInfo->PrefixLength);
    }
  }

  if (IP_VERSION_6 == GetVerFromAddrInfo (AddressInfo)) {
    Print (
      L"%x:%x:%x:%x:%x:%x:%x:%x",
      (((UINT16) AddressInfo->Address.v6.Addr[0]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[1]),
      (((UINT16) AddressInfo->Address.v6.Addr[2]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[3]),
      (((UINT16) AddressInfo->Address.v6.Addr[4]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[5]),
      (((UINT16) AddressInfo->Address.v6.Addr[6]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[7]),
      (((UINT16) AddressInfo->Address.v6.Addr[8]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[9]),
      (((UINT16) AddressInfo->Address.v6.Addr[10]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[11]),
      (((UINT16) AddressInfo->Address.v6.Addr[12]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[13]),
      (((UINT16) AddressInfo->Address.v6.Addr[14]) << 8) | ((UINT16) AddressInfo->Address.v6.Addr[15])
      );
    if (AddressInfo->PrefixLength != 128) {
      Print (L"/%d", AddressInfo->PrefixLength);
    }
  }
}

/**
  Private function called to print EFI_IP_ADDRESS content.

  @param[in] IpAddress    The pointer to the EFI_IP_ADDRESS structure.
**/
VOID
DumpIpAddress (
  IN EFI_IP_ADDRESS    *IpAddress
  )
{
  if (IP_VERSION_4 == GetVerFromIpAddr (IpAddress)) {
    Print (
      L"%d.%d.%d.%d",
      (UINTN) IpAddress->v4.Addr[0],
      (UINTN) IpAddress->v4.Addr[1],
      (UINTN) IpAddress->v4.Addr[2],
      (UINTN) IpAddress->v4.Addr[3]
      );
  }

  if (IP_VERSION_6 == GetVerFromIpAddr (IpAddress)) {
    Print (
      L"%x:%x:%x:%x:%x:%x:%x:%x",
      (((UINT16) IpAddress->v6.Addr[0]) << 8) | ((UINT16) IpAddress->v6.Addr[1]),
      (((UINT16) IpAddress->v6.Addr[2]) << 8) | ((UINT16) IpAddress->v6.Addr[3]),
      (((UINT16) IpAddress->v6.Addr[4]) << 8) | ((UINT16) IpAddress->v6.Addr[5]),
      (((UINT16) IpAddress->v6.Addr[6]) << 8) | ((UINT16) IpAddress->v6.Addr[7]),
      (((UINT16) IpAddress->v6.Addr[8]) << 8) | ((UINT16) IpAddress->v6.Addr[9]),
      (((UINT16) IpAddress->v6.Addr[10]) << 8) | ((UINT16) IpAddress->v6.Addr[11]),
      (((UINT16) IpAddress->v6.Addr[12]) << 8) | ((UINT16) IpAddress->v6.Addr[13]),
      (((UINT16) IpAddress->v6.Addr[14]) << 8) | ((UINT16) IpAddress->v6.Addr[15])
      );
  }

}

/**
  Private function called to print EFI_IPSEC_SPD_SELECTOR content.

  @param[in] Selector    The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
**/
VOID
DumpSpdSelector (
  IN EFI_IPSEC_SPD_SELECTOR    *Selector
  )
{
  UINT32    Index;
  CHAR16    *Str;

  for (Index = 0; Index < Selector->LocalAddressCount; Index++) {
    if (Index > 0) {
      Print (L",");
    }

    DumpAddressInfo (&Selector->LocalAddress[Index]);
  }

  if (Index == 0) {
    Print (L"localhost");
  }

  Print (L" -> ");

  for (Index = 0; Index < Selector->RemoteAddressCount; Index++) {
    if (Index > 0) {
      Print (L",");
    }

    DumpAddressInfo (&Selector->RemoteAddress[Index]);
  }

  Str = MapIntegerToString (Selector->NextLayerProtocol, mMapIpProtocol);
  if (Str != NULL) {
    Print (L" %s", Str);
  } else {
    Print (L" proto:%d", (UINTN) Selector->NextLayerProtocol);
  }

  if ((Selector->NextLayerProtocol == EFI_IP4_PROTO_TCP) || (Selector->NextLayerProtocol == EFI_IP4_PROTO_UDP)) {
    Print (L" port:");
    if (Selector->LocalPort != EFI_IPSEC_ANY_PORT) {
      Print (L"%d", Selector->LocalPort);
      if (Selector->LocalPortRange != 0) {
        Print (L"~%d", (UINTN) Selector->LocalPort + Selector->LocalPortRange);
      }
    } else {
      Print (L"any");
    }

    Print (L" -> ");
    if (Selector->RemotePort != EFI_IPSEC_ANY_PORT) {
      Print (L"%d", Selector->RemotePort);
      if (Selector->RemotePortRange != 0) {
        Print (L"~%d", (UINTN) Selector->RemotePort + Selector->RemotePortRange);
      }
    } else {
      Print (L"any");
    }
  } else if (Selector->NextLayerProtocol == EFI_IP4_PROTO_ICMP) {
    Print (L" class/code:");
    if (Selector->LocalPort != 0) {
      Print (L"%d", (UINTN) (UINT8) Selector->LocalPort);
    } else {
      Print (L"any");
    }

    Print (L"/");
    if (Selector->RemotePort != 0) {
      Print (L"%d", (UINTN) (UINT8) Selector->RemotePort);
    } else {
      Print (L"any");
    }
  }
}

/**
  Print EFI_IPSEC_SPD_SELECTOR and EFI_IPSEC_SPD_DATA content.

  @param[in] Selector      The pointer to the EFI_IPSEC_SPD_SELECTOR structure.
  @param[in] Data          The pointer to the EFI_IPSEC_SPD_DATA structure.
  @param[in] EntryIndex    The pointer to the Index in SPD Database.

  @retval EFI_SUCCESS    Dump SPD information successfully.
**/
EFI_STATUS
DumpSpdEntry (
  IN EFI_IPSEC_SPD_SELECTOR    *Selector,
  IN EFI_IPSEC_SPD_DATA        *Data,
  IN UINTN                     *EntryIndex
  )
{
  BOOLEAN    HasPre;
  CHAR16     DataName[128];
  CHAR16     *String1;
  CHAR16     *String2;
  CHAR16     *String3;
  UINT8      Index;

  Print (L"%d.", (*EntryIndex)++);

  //
  // xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400
  // Protect  PF:0x34323423 Name:First Entry
  // ext-sequence sequence-overflow fragcheck life:[B0,S1024,H3600]
  // ESP algo1 algo2 Tunnel [xxx.xxx.xxx.xxx xxx.xxx.xxx.xxx set]
  //

  DumpSpdSelector (Selector);
  Print (L"\n  ");

  Print (L"%s ", MapIntegerToString (Data->Action, mMapIpSecAction));
  Print (L"PF:%08x ", Data->PackageFlag);

  Index = 0;
  while (Data->Name[Index] != 0) {
    DataName[Index] = (CHAR16) Data->Name[Index];
    Index++;
    ASSERT (Index < 128);
  }
  DataName[Index] = L'\0';

  Print (L"Name:%s", DataName);

  if (Data->Action == EfiIPsecActionProtect) {
    Print (L"\n  ");
    if (Data->ProcessingPolicy->ExtSeqNum) {
      Print (L"ext-sequence ");
    }

    if (Data->ProcessingPolicy->SeqOverflow) {
      Print (L"sequence-overflow ");
    }

    if (Data->ProcessingPolicy->FragCheck) {
      Print (L"fragment-check ");
    }

    HasPre = FALSE;
    if (Data->ProcessingPolicy->SaLifetime.ByteCount != 0) {
      Print (HasPre ? L"," : L"life:[");
      Print (L"%lxB", Data->ProcessingPolicy->SaLifetime.ByteCount);
      HasPre = TRUE;
    }

    if (Data->ProcessingPolicy->SaLifetime.SoftLifetime != 0) {
      Print (HasPre ? L"," : L"life:[");
      Print (L"%lxs", Data->ProcessingPolicy->SaLifetime.SoftLifetime);
      HasPre = TRUE;
    }

    if (Data->ProcessingPolicy->SaLifetime.HardLifetime != 0) {
      Print (HasPre ? L"," : L"life:[");
      Print (L"%lxS", Data->ProcessingPolicy->SaLifetime.HardLifetime);
      HasPre = TRUE;
    }

    if (HasPre) {
      Print (L"]");
    }

    if (HasPre || Data->ProcessingPolicy->ExtSeqNum ||
        Data->ProcessingPolicy->SeqOverflow || Data->ProcessingPolicy->FragCheck) {
      Print (L"\n  ");
    }

    String1 = MapIntegerToString (Data->ProcessingPolicy->Proto, mMapIpSecProtocol);
    String2 = MapIntegerToString (Data->ProcessingPolicy->AuthAlgoId, mMapAuthAlgo);
    String3 = MapIntegerToString (Data->ProcessingPolicy->EncAlgoId, mMapEncAlgo);
    Print (
      L"%s Auth:%s Encrypt:%s ",
      String1,
      String2,
      String3
      );

    Print (L"%s ", MapIntegerToString (Data->ProcessingPolicy->Mode, mMapIpSecMode));
    if (Data->ProcessingPolicy->Mode == EfiIPsecTunnel) {
      Print (L"[");
      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->LocalTunnelAddress);
      Print (L" -> ");
      DumpIpAddress (&Data->ProcessingPolicy->TunnelOption->RemoteTunnelAddress);
      Print (L" %s]", MapIntegerToString (Data->ProcessingPolicy->TunnelOption->DF, mMapDfOption));
    }
  }

  Print (L"\n");

  return EFI_SUCCESS;
}

/**
  Print EFI_IPSEC_SA_ID and EFI_IPSEC_SA_DATA2 content.

  @param[in] SaId          The pointer to the EFI_IPSEC_SA_ID structure.
  @param[in] Data          The pointer to the EFI_IPSEC_SA_DATA2 structure.
  @param[in] EntryIndex    The pointer to the Index in the SAD Database.

  @retval EFI_SUCCESS    Dump SAD information successfully.
**/
EFI_STATUS
DumpSadEntry (
  IN EFI_IPSEC_SA_ID      *SaId,
  IN EFI_IPSEC_SA_DATA2   *Data,
  IN UINTN                *EntryIndex
  )
{
  BOOLEAN    HasPre;
  CHAR16     *AuthAlgoStr;
  CHAR16     *EncAlgoStr;

  AuthAlgoStr      = NULL;
  EncAlgoStr       = NULL;

  //
  // SPI:1234 ESP Destination:xxx.xxx.xxx.xxx
  //  Mode:Transport SeqNum:134 AntiReplayWin:64 life:[0B,1023s,3400S] PathMTU:34
  //  Auth:xxxx/password Encrypt:yyyy/password
  //  xxx.xxx.xxx.xxx/yy -> xxx.xxx.xxx.xx/yy proto:23 port:100~300 -> 300~400
  //

  Print (L"%d.", (*EntryIndex)++);
  Print (L"0x%x %s ", (UINTN) SaId->Spi, MapIntegerToString (SaId->Proto, mMapIpSecProtocol));
  if (Data->Mode == EfiIPsecTunnel) {
    Print (L"TunnelSourceAddress:");
    DumpIpAddress (&Data->TunnelSourceAddress);
    Print (L"\n");
    Print (L"  TunnelDestination:");
    DumpIpAddress (&Data->TunnelDestinationAddress);
    Print (L"\n");
  }

  Print (
    L"  Mode:%s SeqNum:%lx AntiReplayWin:%d ",
    MapIntegerToString (Data->Mode, mMapIpSecMode),
    Data->SNCount,
    (UINTN) Data->AntiReplayWindows
    );

  HasPre = FALSE;
  if (Data->SaLifetime.ByteCount != 0) {
    Print (HasPre ? L"," : L"life:[");
    Print (L"%lxB", Data->SaLifetime.ByteCount);
    HasPre = TRUE;
  }

  if (Data->SaLifetime.SoftLifetime != 0) {
    Print (HasPre ? L"," : L"life:[");
    Print (L"%lxs", Data->SaLifetime.SoftLifetime);
    HasPre = TRUE;
  }

  if (Data->SaLifetime.HardLifetime != 0) {
    Print (HasPre ? L"," : L"life:[");
    Print (L"%lxS", Data->SaLifetime.HardLifetime);
    HasPre = TRUE;
  }

  if (HasPre) {
    Print (L"] ");
  }

  Print (L"PathMTU:%d\n", (UINTN) Data->PathMTU);

  if (SaId->Proto == EfiIPsecAH) {
    Print (
      L"  Auth:%s/%s\n",
      MapIntegerToString (Data->AlgoInfo.AhAlgoInfo.AuthAlgoId, mMapAuthAlgo),
      Data->AlgoInfo.AhAlgoInfo.AuthKey
      );
  } else {
    AuthAlgoStr = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.AuthAlgoId, mMapAuthAlgo);
    EncAlgoStr  = MapIntegerToString (Data->AlgoInfo.EspAlgoInfo.EncAlgoId, mMapEncAlgo);

    if (Data->ManualSet) {
      //
      // if the SAD is set manually the key is a Ascii string in most of time.
      // Print the Key in Ascii string format.
      //
      Print (L"  Auth:%s/",AuthAlgoStr);
      DumpAsciiString (
        Data->AlgoInfo.EspAlgoInfo.AuthKey,
        Data->AlgoInfo.EspAlgoInfo.AuthKeyLength
        );
      Print (L"\n  Encrypt:%s/",EncAlgoStr);
      DumpAsciiString (
        Data->AlgoInfo.EspAlgoInfo.EncKey,
        Data->AlgoInfo.EspAlgoInfo.EncKeyLength
        );
    } else {
      //
      // if the SAD is created by IKE, the key is a set of hex value in buffer.
      // Print the Key in Hex format.
      //
      Print (L"  Auth:%s/",AuthAlgoStr);
      DumpBuf ((UINT8 *)(Data->AlgoInfo.EspAlgoInfo.AuthKey), Data->AlgoInfo.EspAlgoInfo.AuthKeyLength);

      Print (L"\n  Encrypt:%s/",EncAlgoStr);
      DumpBuf ((UINT8 *)(Data->AlgoInfo.EspAlgoInfo.EncKey), Data->AlgoInfo.EspAlgoInfo.EncKeyLength);
    }
  }
  Print (L"\n");
  if (Data->SpdSelector != NULL) {
    Print (L"  ");
    DumpSpdSelector (Data->SpdSelector);
    Print (L"\n");
  }

  return EFI_SUCCESS;
}

/**
  Print EFI_IPSEC_PAD_ID and EFI_IPSEC_PAD_DATA content.

  @param[in] PadId         The pointer to the EFI_IPSEC_PAD_ID structure.
  @param[in] Data          The pointer to the EFI_IPSEC_PAD_DATA structure.
  @param[in] EntryIndex    The pointer to the Index in the PAD Database.

  @retval EFI_SUCCESS    Dump PAD information successfully.
**/
EFI_STATUS
DumpPadEntry (
  IN EFI_IPSEC_PAD_ID      *PadId,
  IN EFI_IPSEC_PAD_DATA    *Data,
  IN UINTN                 *EntryIndex
  )
{
  CHAR16    *String1;
  CHAR16    *String2;

  //
  // ADDR:10.23.17.34/15
  // IDEv1 PreSharedSecret IKE-ID
  // password
  //

  Print (L"%d.", (*EntryIndex)++);

  if (PadId->PeerIdValid) {
    Print (L"ID:%s", PadId->Id.PeerId);
  } else {
    Print (L"ADDR:");
    DumpAddressInfo (&PadId->Id.IpAddress);
  }

  Print (L"\n");

  String1 = MapIntegerToString (Data->AuthProtocol, mMapAuthProto);
  String2 = MapIntegerToString (Data->AuthMethod, mMapAuthMethod);
  Print (
    L"  %s %s",
    String1,
    String2
    );

  if (Data->IkeIdFlag) {
    Print (L"IKE-ID");
  }

  Print (L"\n");

  if (Data->AuthData != NULL) {
    DumpAsciiString (Data->AuthData, Data->AuthDataSize);
    Print (L"\n");
  }

  if (Data->RevocationData != NULL) {
    Print (L"  %s\n", Data->RevocationData);
  }

  return EFI_SUCCESS;

}

VISIT_POLICY_ENTRY  mDumpPolicyEntry[] = {
  (VISIT_POLICY_ENTRY) DumpSpdEntry,
  (VISIT_POLICY_ENTRY) DumpSadEntry,
  (VISIT_POLICY_ENTRY) DumpPadEntry
};

/**
  Print all entry information in the database according to datatype.

  @param[in] DataType        The value of EFI_IPSEC_CONFIG_DATA_TYPE.
  @param[in] ParamPackage    The pointer to the ParamPackage list.

  @retval EFI_SUCCESS    Dump all information successfully.
  @retval Others         Some mistaken case.
**/
EFI_STATUS
ListPolicyEntry (
  IN EFI_IPSEC_CONFIG_DATA_TYPE    DataType,
  IN LIST_ENTRY                    *ParamPackage
  )
{
  UINTN  EntryIndex;

  EntryIndex = 0;
  return ForeachPolicyEntry (DataType, mDumpPolicyEntry[DataType], &EntryIndex);
}

